﻿
using System.Web.Http;

using SimpleInjector;
using Sustainalytics.Entities;
using SimpleInjector.Integration.WebApi;
using System.Configuration.Abstractions;
using Sustainalytics.MongoDB.Extensions;
using Sustainalytics.ClientDW.Entities;
using Sustainalytics.Portfolios.BL;
using Sustainalytics.DataLayer;
using Sustainalytics.DataLayer.Shared;
using System.Collections.Generic;
using Sustainalytics.Utils.Validation.Interfaces;
using SpreadsheetGear;
using Sustainalytics.OAuth.Services.Common;
using Sustainalytics.Claims;
using Sustainalytics.Companies;
using System;
using Sustainalytics.DataLayer.EsgRatings;
using Sustainalytics.Entities.ESGRatings;
using Sustainalytics.Replication.Publisher;
using Sustainalytics.Controversies.BusinessLogic;
using MongoDB.Driver;

namespace Sustainalytics.Portfolios.Service
{


    public static class PortfolioCompositionRoot
    {
        private static readonly string ClientPlatformConnectionString = ConfigurationManager.Instance.ConnectionStrings["ClientPlatform"].ConnectionString;
        
        public static Container RegisterAsync(HttpConfiguration config)
        {
            var container = new Container();
            container.Options.DefaultScopedLifestyle = new SimpleInjector.Lifestyles.AsyncScopedLifestyle();
            container.RegisterWebApiControllers(config);
            container.Register(() => ConfigurationManager.Instance.AppSettings);
            container.RegisterFactories(ClientPlatformConnectionString);
            container.RegisterServiceBusParams();
            container.RegisterHandlers();
            container.RegisterPropertyDependency();
            container.RegisterValidators();
            container.RegisterSingleton(new PortfolioUserClaimsProcessor());
            container.RegisterSingleton<IClaimsChecker>(new ClaimsChecker());
            container.RegisterRepositories(ClientPlatformConnectionString);
            container.RegisterCollections();
            container.RegisterBussinessLogic();
            container.Verify();
            return container;
        }

        private static void RegisterFactories(this Container container, string clientPlatformConnectionString)
        {
            container.RegisterSingleton(new RepositoryCollectionFactory<Portfolio> {
                        { RegistrationTypes.Default, () => new RepositoryCollection<Portfolio>(clientPlatformConnectionString) },
                        { RegistrationTypes.ClientDW, () => new RepositoryCollection<Portfolio>(MongoFactory.GetConnectionStrings().ClientDW) }});

            container.RegisterSingleton(new RepositoryCollectionFactory<PortfolioDetails> {
                        { RegistrationTypes.Default, () => new RepositoryCollection<PortfolioDetails>(clientPlatformConnectionString) },
                        { RegistrationTypes.ClientDW, () => new RepositoryCollection<PortfolioDetails>(MongoFactory.GetConnectionStrings().ClientDW) }});

            container.RegisterSingleton(new RepositoryCollectionFactory<PortfolioUserInput> {
                        { RegistrationTypes.Default, () => new RepositoryCollection<PortfolioUserInput>(clientPlatformConnectionString) },
                        { RegistrationTypes.ClientDW, () => new RepositoryCollection<PortfolioUserInput>(MongoFactory.GetConnectionStrings().ClientDW) }});
        }
        private static void RegisterValidators(this Container container)
        {
            container.Register(typeof(IValidator<IWorksheet>), typeof(PortfolioWorksheetValidator));
        }

        private static void RegisterServiceBusParams(this Container container)
        {
            container.RegisterSingleton(() =>
            {
                var serviceBusParams = new Dictionary<string, string> {
                    { "ConnectionString", container.GetInstance<IAppSettings>().Get("Microsoft.ServiceBus.ConnectionString") },
                    { "RegionEndpoint", container.GetInstance<IAppSettings>().Get("Microsoft.ServiceBus.RegionEndpoint") },
                    { "SessionId", container.GetInstance<IAppSettings>().Get("Microsoft.ServiceBus.SessionId") },
                    { "TopicPath", container.GetInstance<IAppSettings>().Get("Microsoft.ServiceBus.TopicPath") } };

            return new ServiceBusPublisher(serviceBusParams);
            });
        }

        private static void RegisterRepositories(this Container container, string clientPlatform)
        {
            container.Register(typeof(PortfolioTypeCollection<Portfolio>), () =>
            {
                var factory = container.GetInstance<RepositoryCollectionFactory<Portfolio>>();
                return new PortfolioTypeCollection<Portfolio>(
                    factory.CreateNew<Portfolio>(RegistrationTypes.Default), 
                    factory.CreateNew<Portfolio>(RegistrationTypes.ClientDW));
            });

            container.Register(typeof(PortfolioTypeCollection<PortfolioDetails>), () =>
            {
                var factory = container.GetInstance<RepositoryCollectionFactory<PortfolioDetails>>();
                return new PortfolioTypeCollection<PortfolioDetails>(
                    factory.CreateNew<PortfolioDetails>(RegistrationTypes.Default), 
                    factory.CreateNew<PortfolioDetails>(RegistrationTypes.ClientDW));
            });

            container.Register(typeof(PortfolioTypeCollection<PortfolioUserInput>), () =>
            {
                var factory = container.GetInstance<RepositoryCollectionFactory<PortfolioUserInput>>();
                return new PortfolioTypeCollection<PortfolioUserInput>(
                    factory.CreateNew<PortfolioUserInput>(RegistrationTypes.Default), 
                    factory.CreateNew<PortfolioUserInput>(RegistrationTypes.ClientDW));
            });


            container.Register(typeof(IRepositoryCollection<Portfolio>), () => new RepositoryCollection<Portfolio>(clientPlatform));
            container.Register(typeof(IRepositoryCollection<PortfolioDetails>), () => new RepositoryCollection<PortfolioDetails>(clientPlatform));
            container.Register(typeof(IRepositoryCollection<PortfolioMatches>), () => new RepositoryCollection<PortfolioMatches>(clientPlatform));
            

            container.Register(typeof(IRepositoryCollection<PortfolioUserInput>), () => new RepositoryCollection<PortfolioUserInput>(clientPlatform));
            container.Register(typeof(IRepositoryCollection<EsgInputFile>), () => new RepositoryCollection<EsgInputFile>(clientPlatform));

            container.Register(typeof(IFileRepository), () => new FileRepository(clientPlatform));

            container.Register(typeof(IEsgInputFileRepository),typeof(EsgInputFileRepository));

            container.Register(typeof(ISubscribeToPortfolioRepository), () => new SubscribeToPortfolioRepository(clientPlatform));
            container.Register(typeof(IRepositoryCollection<Filter>), () => new RepositoryCollection<Filter>(clientPlatform));
            container.Register(typeof(IAccountsRepository), () => { return AccountsRepository.GetAccountsRepository(clientPlatform); });
            container.Register(typeof(IRepositoryCollection<CorporateDataBasic>), () => {
                return new RepositoryCollection<CorporateDataBasic>(MongoFactory.GetConnectionStrings().ClientDW, "CorporateData"); });
        }

        private static void RegisterHandlers(this Container container)
        {
            container.Register(typeof(IPortfolioReadHandler), typeof(PortfolioReadHandler));
            container.Register(typeof(ICustomPortfolioUpdateHandler), typeof(CustomPortfolioUpdateHandler));

            container.Register(typeof(IPortfolioSecuritiesImportManagement), typeof(PortfolioSecuritiesImportManagement));
            container.Register(typeof(ISubscribeToPortfolioManagement), typeof(SubscribeToPortfolioManagement));
            container.Register(typeof(IPortfolioRenameHandler), typeof(PortfolioRenameHandler));
            container.Register(typeof(IPortfolioDeleteHandler), typeof(PortfolioDeleteHandler));
            container.Register(typeof(IScreeningToolFilterManager), typeof(ScreeningToolFilterManager));
            container.Register(typeof(IManualSystemPortfolioUpdatesHandler), typeof(ManualSystemPortfolioUpdatesHandler));
            container.RegisterDecorator<IManualSystemPortfolioUpdatesHandler, ReplicationHandler>(Lifestyle.Singleton);
            container.Register(typeof(PortfolioExportManagement));
            container.Register<PortfolioMatching>();
            container.Register<CorporateDataUniversesLoader>();
        }

        private static void RegisterPropertyDependency(this Container container)
        {
            container.RegisterInitializer<ICustomPortfolioUpdateHandler>(
                handler =>
                {
                    handler.SetPortfolioMatching(container.GetInstance<PortfolioMatching>());
                    handler.SetScreeningToolFilterManagement(container.GetInstance<ScreeningToolFilterManager>());
                    handler.SetSubscriptionManager(container.GetInstance<SubscribeToPortfolioManagement>());
                });
        }

        private static void RegisterCollections(this Container container)
        {
            container.Register(() => MongoFactory.GetCollectionFromClientPlatform<ConnectionStrings>());
            container.Register(() => MongoFactory.GetCollectionFromDatabase<CorporateData>(x => x.ClientDW));
        }

        private static void RegisterBussinessLogic(this Container container)
        {
            container.Register((typeof(ICompanyMatchingLogic)), () => new CompanyMatchingLogic(container.GetInstance<IMongoCollection<CorporateData>>()));
        }
    }

    public enum RegistrationTypes
    {
        Default = 0,
        ClientDW
    }

    public interface IRepositoryCollectionFactory
    {
        IRepositoryCollection<T> CreateNew<T>(RegistrationTypes name) where T : IEntity;
    }

    public class RepositoryCollectionFactory<T> : Dictionary<RegistrationTypes, Func<IRepositoryCollection<T>>>, IRepositoryCollectionFactory where T : IEntity
    {
        public IRepositoryCollection<T> CreateNew<T>(RegistrationTypes name) where T : IEntity
        {
            if (!this.ContainsKey(name))
                return (IRepositoryCollection<T>)this[RegistrationTypes.Default]();

            return (IRepositoryCollection<T>)this[name]();
        }
    }


}
